{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# PYNQ tutorial: Allocate and Contiguous Memory Allocation\n",
    "\n",
    "`allocate()` assigns a contiguous block of memory. The data in the memory buffer can be transferred efficiently between the PS and the PL. \n",
    "\n",
    "Note: The `allocate()` driver is overlay-agnostic, meaning it can be used no matter what overlay you are using.\n",
    "\n",
    "## Preparation\n",
    "\n",
    "In the following cell we will define a useful function to check free memory."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def free_mem():\n",
    "    mem = !cat /proc/meminfo | grep 'MemFree'\n",
    "    print(mem)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "## Step 1: check free memory"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "free_mem()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 2: allocate the memory\n",
    "allocate instance will be labeled `buffer`. The allocate class take at least one argument, which is the size. Optionally you can define the data type"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pynq import allocate\n",
    "import numpy as np \n",
    "buffer = allocate(shape=(10000000,), dtype=np.float32)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 3: check free memory after allocation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "free_mem()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 4: Check the memory buffer address\n",
    "\n",
    "The virtual address can be used by Linux. The Physical address can be passed to a peripheral in the PL."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Buffer pointer address (physical memory):\")\n",
    "print(hex(buffer.physical_address))\n",
    "print(\"Buffer pointer address (virtual memory):\")\n",
    "print(hex(buffer.virtual_address))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 4: Free the memory\n",
    "It is always a good practice to free the contiguous memory after use. This prevents memory leaks from the program."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "del buffer\n",
    "free_mem()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> Note, it is normal that the available memory may not be exactly the same as the previous number. "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
